/* -*-C-*-
 ##############################################################################
 #
 # File:        trice/src/meas.c
 # RCS:         "@(#)$Revision: 1.20 $ $Date: 94/03/09 11:09:42 $"
 # Description: User routines for measurement functions in E1430 module
 # Author:      Doug Passey
 # Created:     
 # Language:    C
 # Package:     E1430
 # Status:      "@(#)$State: Exp $"
 #
 # (C) Copyright 1992, Hewlett-Packard Company, all rights reserved.
 #
 ##############################################################################
 #
 # Please add additional comments here
 #
 # Revisions:
 #   modified 2/27/94 to fix bug LSIlz37628 - by Mike Aken
 #
 ##############################################################################
*/

#    include <stdio.h>

#include "trice.h"
#include "err1430.h"

#ifndef lint
const char meas_fileId[] = "$Header: meas.c,v 1.20 94/03/09 11:09:42 chriss Exp $";
#endif

/*****************************************************************************
 *
 * Read status and update SCPI status stuff if necessary.  Returns
 * error number if error, otherwise returns 0.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_get_status(SHORTSIZ16 la, SHORTSIZ16 *statusPtr)
{
  SHORTSIZ16 error;

  error = e1430_read_register_card(la, E1430_VXI_STATUS_REG, statusPtr);
  if(error) return(error);

  if(*statusPtr != e1430_cur_status) {
    e1430_cur_status = *statusPtr;
  }

  return(0);
}


/*****************************************************************************
 *
 * Hard reset module. 
 *
 ****************************************************************************/
SHORTSIZ16 e1430_hard_reset(SHORTSIZ16 la)
{
  
  e1430_write_register_card(la, E1430_VXI_CONTROL_REG, VXI_CONTROL_RESET_ON);

  e1430_write_register_card(la, E1430_VXI_CONTROL_REG, VXI_CONTROL_RESET_OFF);

  e1430_pause(0.1);	/* need for case when resetting from external clock */

  return(0);
}


/*****************************************************************************
 *
 * Resets all modules in groupID to their power up state.  Returns negative
 * error number if error, otherwise returns 0.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_reset_module(SHORTSIZ16 groupID)
{
  SHORTSIZ16 error, status;
  aModGroupPtr ptr, firstPtr;

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {/* no such group */
    return (ERR1430_NO_GROUP);
  }

  /* hard reset */
  for(ptr=firstPtr; *ptr != -1; ptr++) {	/* put all modules in reset */
    e1430_write_register_card(e1430_modStates[*ptr].logicalAddr, 
				E1430_VXI_CONTROL_REG, VXI_CONTROL_RESET_ON);
  }

  for(ptr=firstPtr; *ptr != -1; ptr++) {	/* take them out of reset */
    e1430_write_register_card(e1430_modStates[*ptr].logicalAddr, 
				E1430_VXI_CONTROL_REG, VXI_CONTROL_RESET_OFF);
  }

  e1430_pause(0.1);	/* need for case when resetting from external clock */

  error = e1430_reset_dsp(groupID);
  if(error) return(error);

  for(ptr=firstPtr; *ptr != -1; ptr++) {	/* reset all modules in group */
    /* DO THESE STEPS IN THIS ORDER */
    (void) i1430_init_mod_state(e1430_modStates[*ptr].logicalAddr, &(e1430_modStates[*ptr]));
    (void) i1430_reset_E1430(e1430_modStates[*ptr].logicalAddr);

    error = e1430_read_register_card(e1430_modStates[*ptr].logicalAddr,
					E1430_VXI_STATUS_REG, &status);
    if(error) return(error);
  }
 
  return (0);
}


/*****************************************************************************
 *
 * Return logical address of module with master clock set into memory location
 * pointed to by <masterLa>. Returns 0 if successful, otherwise an error.
 *
 ****************************************************************************/
static SHORTSIZ16 i1430_get_master_la(SHORTSIZ16 groupID, SHORTSIZ16 *masterLa)
{
  char buf[80];
  aModGroupPtr ptr, firstPtr;

  *masterLa = 0;

  if((firstPtr = i1430_valid_module_group(groupID)) == NULL) {
    return (ERR1430_NO_GROUP);
  }

  ptr = firstPtr;
  ptr++;		/* point to next module in group */

  /* determine which module is the clock master */
  if(*ptr != -1) {		/* if more than one module */ 	
    /* look for the module who is the clock master*/
    for(ptr = firstPtr; *ptr != -1; ptr++) {
      if((e1430_modStates[*ptr].timingSetup & ~TIMING_SETUP_MASTER_MASK ) 
					== TIMING_SETUP_MASTER_ON) {
	*masterLa = e1430_modStates[*ptr].logicalAddr;
      }

      /* if not all modules in group are multi sync, this is an error */
      if((e1430_modStates[*ptr].timingSetup & ~TIMING_SETUP_MULTI_SYNC_MASK ) 
					== TIMING_SETUP_MULTI_SYNC_OFF) {
        (void)sprintf(buf, "%d", (LONGSIZ32)groupID);
        return(i1430_Error(ERR1430_NOT_SYNCHRONOUS_GROUP, buf, NULL));
      }
    }

    if(*masterLa == 0) {		/* didn't find a master */
    *masterLa = e1430_modStates[*firstPtr].logicalAddr;
    }

             /* NOTE: this code was modified 2/27/94 in response to
                bug LSIlz37628 ( allow no master ) */



  }else{    /* only one module in group; so it is master by definition */
    *masterLa = e1430_modStates[*firstPtr].logicalAddr;
  }

  return(0);
}


/*****************************************************************************
 *
 * Arm a group of modules in <groupID>.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
static SHORTSIZ16 arm_module(SHORTSIZ16 groupID, SHORTSIZ16 releaseSync)
{
  SHORTSIZ16 done, status;
  aModGroupPtr ptr, firstPtr; 
  SHORTSIZ16 error = 0;
  FLOATSIZ64 startTime, currentTime;
  LONGSIZ32 timeout;
  SHORTSIZ16 masterLa;

  if((ptr = i1430_valid_module_group(groupID)) == NULL) {/* no such group exists */
    return (ERR1430_NO_GROUP);
  }

  firstPtr = ptr;	/* pointer to first module in group */

  error = i1430_get_master_la(groupID, &masterLa);
  if(error) return(error);

  error =  i1430_pause_meas(groupID);		/* force all modules to IDLE state */
  if(error) return (error);

  /* release sync line then pull low on master module */
  error = i1430_release_sync(masterLa, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);

  error = i1430_pull_sync(masterLa, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);

  (void)i1430_get_time(&startTime);
  e1430_get_timeout(&timeout);
  do {			/* wait for all modules to be in ARM state */
    done = 1;

    for(ptr = firstPtr; *ptr != -1; ptr++) {
      error = e1430_get_status(e1430_modStates[*ptr].logicalAddr, &status);
      if(error) return(error);

      if((status & E1430_MEAS_STATUS_STATE_MASK) != 
						E1430_MEAS_STATUS_ARM_STATE) {
	done = 0;
      }
    }

    if(e1430_device_clear_flag) {		/* device clear issued in SCPI */
      (void)e1430_abort_meas(groupID);
      return(0);
    }

    (void)i1430_get_time(&currentTime);
    if(currentTime - startTime > (FLOATSIZ64)timeout) { /* time out, release SYNC */
      (void)i1430_release_sync(masterLa, MEAS_CONTROL_NORMAL_MODE);
      return (i1430_Error(ERR1430_ARM_TIMEOUT, NULL, NULL));
    }
  }while (!done);

  if(releaseSync) {
    /* pull SYNC high on master module for modules to enter TRIGGER state */
    error = i1430_release_sync(masterLa, MEAS_CONTROL_NORMAL_MODE);
  }

  return (error);
}

 
/*****************************************************************************
 *
 * Arm a group of modules in <groupID>.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_arm_module(SHORTSIZ16 groupID)
{
  return(arm_module(groupID, 1));
}


/*****************************************************************************
 *
 * Forces module group into IDLE state with SYNC line held low.
 * Returns negative error number if error, otherwise it returns 0.
 *
 ****************************************************************************/
SHORTSIZ16  i1430_pause_meas(SHORTSIZ16 groupID)
{
  aModGroupPtr ptr;
  SHORTSIZ16 error;
  SHORTSIZ16 masterLa;

  if((ptr = i1430_valid_module_group(groupID)) == NULL) {/* no such group exists */
    return (ERR1430_NO_GROUP);
  }

  /* get master module logical address */
  error = i1430_get_master_la(groupID, &masterLa);
  if(error) return(error);

  /* pull the sync line on master module and force into IDLE */
  error = i1430_pull_sync(masterLa, MEAS_CONTROL_IDLE_MODE);
  if(error) return (error);

  /* do it again for good measure ... it doesn't always work the first time */
  error = i1430_pull_sync(masterLa, MEAS_CONTROL_IDLE_MODE);
  if(error) return (error);

  /* put other modules in idle mode */
  for( ; *ptr != -1; ptr++) {
    if(e1430_modStates[*ptr].logicalAddr != masterLa) {
      error = e1430_write_register_image(e1430_modStates[*ptr].logicalAddr, 
			E1430_MEAS_CONTROL_REG, MEAS_CONTROL_IDLE_MODE);
      if(error) return (error);
    }
  }
    
  return (0);
}


/*****************************************************************************
 *
 * Releases SYNC line held low by first module in group.
 * Returns negative error number if error, otherwise it returns 0.
 *
 ****************************************************************************/
SHORTSIZ16 i1430_continue_meas(SHORTSIZ16 groupID)
{

  SHORTSIZ16 masterLa;
  SHORTSIZ16 error;

  error = i1430_get_master_la(groupID, &masterLa);
  if(error) return(error);

  return(i1430_release_sync(masterLa, MEAS_CONTROL_NORMAL_MODE));
}


/*****************************************************************************
 *
 * Returns all modules to IDLE state
 * Returns negative error number if error, otherwise it returns 0.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_abort_meas(SHORTSIZ16 groupID)
{
  SHORTSIZ16 error;

  error =  i1430_pause_meas(groupID);
  if(error) return(error);

  error = arm_module(groupID, 0);	/* make sure no data xfer to lbus */
  if(error) return(error);

  error =  i1430_pause_meas(groupID);
  if(error) return(error);

  return(i1430_continue_meas(groupID));
}


/*****************************************************************************
 *
 * Trigger the modules in the group <groupID>.  Waits for all modules to be
 * in trigger state and then triggers them.
 * Returns 0 if all ok, else return negative error number.
 *
 ****************************************************************************/
SHORTSIZ16 e1430_trigger_module(SHORTSIZ16 groupID)
{
  aModGroupPtr firstPtr, ptr;
  SHORTSIZ16 done;
  SHORTSIZ16 stat;
  SHORTSIZ16 error;
  FLOATSIZ64 startTime, currentTime;
  LONGSIZ32 timeout;

  firstPtr = i1430_valid_module_group(groupID);
  if(NULL == firstPtr) {
    return (ERR1430_NO_GROUP);
  }
 
  (void)i1430_get_time(&startTime);
  e1430_get_timeout(&timeout);
  do {			/* loop until all are in trigger state */
    done = 1;
    for(ptr = firstPtr; *ptr != -1; ptr++) {
      error = e1430_get_status(e1430_modStates[*ptr].logicalAddr, &stat);
      if(error) return(error);

      if((stat & E1430_MEAS_STATUS_STATE_MASK) != 
				E1430_MEAS_STATUS_TRIG_STATE) {
	done = 0;
      }
    }
    
    (void)i1430_get_time(&currentTime);
    if(currentTime - startTime > (FLOATSIZ64)timeout) { /* time out */
      return (i1430_Error(ERR1430_TRIG_TIMEOUT, NULL, NULL));
    }
  } while (!done);

  
  error = i1430_pull_sync(e1430_modStates[*firstPtr].logicalAddr, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);

  (void)i1430_get_time(&startTime);
  do {			/* loop until all are in meas state */
    done = 1;
    for(ptr = firstPtr; *ptr != -1; ptr++) {
      error = e1430_get_status(e1430_modStates[*ptr].logicalAddr, &stat);
      if(error) return(error);

      if((stat & E1430_MEAS_STATUS_STATE_MASK) != E1430_MEAS_STATUS_MEAS_STATE){
	done = 0;
      }
    }
    
    (void)i1430_get_time(&currentTime);
    if(currentTime - startTime > (FLOATSIZ64)timeout) { /* time out */
      return (i1430_Error(ERR1430_TRIG_TIMEOUT, NULL, NULL));
    }
  } while (!done);

  error = i1430_release_sync(e1430_modStates[*firstPtr].logicalAddr, 
						MEAS_CONTROL_NORMAL_MODE);

  return (error);
}


/****************************************************************************
 *
 * Wait for block of data to be ready on module with logical address <la>.
 *
 * Returns error for a timeout.
 *
 ***************************************************************************/
SHORTSIZ16 i1430_wait_for_block_ready_la(SHORTSIZ16 la, SHORTSIZ16 *status)
{
  FLOATSIZ64 startTime, currentTime;
  LONGSIZ32 timeout;
  char buf[80];
  SHORTSIZ16 temp, error, mask;

  /* data ready depends on which mode we are in */
  error = e1430_read_register_image(la, E1430_DATA_FORMAT_REG, &temp);
  if(error) return(error);

  if((temp & ~DATA_FORMAT_BLOCK_MODE_MASK) == DATA_FORMAT_BLOCK_MODE_ON) {
    mask = E1430_MEAS_STATUS_MEAS_DONE;
  }else{
    mask = E1430_MEAS_STATUS_BLOCK_READY;
  }

  *status = 0;			/* clear status return value */
  (void)i1430_get_time(&startTime);
  e1430_get_timeout(&timeout);
  do {			/* wait for block ready flag */ 
    error = e1430_get_status(la, &temp);
    if(error) return(error);

    *status |= temp & 
		(E1430_MEAS_STATUS_OVERLOAD | E1430_MEAS_STATUS_ADC_ERROR);

    if(e1430_device_clear_flag) return(-1);

    (void)i1430_get_time(&currentTime);
    if(currentTime - startTime > (FLOATSIZ64)timeout) {
      (void)sprintf(buf, "%d", (LONGSIZ32)la);
      return(i1430_Error(ERR1430_DATA_READ_TIMEOUT, buf, NULL));
    }
  } while (!(temp & mask));

  return (0);
}

/****************************************************************************
 *
 * Pull the SYNC line on module at <la>.
 *
 * Returns error for a timeout.
 *
 ***************************************************************************/
SHORTSIZ16 i1430_pull_sync(SHORTSIZ16 la, SHORTSIZ16 opcode)
{
  SHORTSIZ16 error;

  error = e1430_write_register_image(la, E1430_MEAS_CONTROL_REG,
						MEAS_CONTROL_SYNC_ON | opcode);
  if(error) return(error);

  error = i1430_wait_for_sync_valid(la);

  return(error);
}


/****************************************************************************
 *
 * Release the SYNC line on module at <la>.
 *
 * Returns error for a timeout.
 *
 ***************************************************************************/
SHORTSIZ16 i1430_release_sync(SHORTSIZ16 la, SHORTSIZ16 opcode)
{
  SHORTSIZ16 error;

  error = e1430_write_register_image(la, E1430_MEAS_CONTROL_REG,
						MEAS_CONTROL_SYNC_OFF | opcode);
  if(error) return(error);

  error = i1430_wait_for_sync_valid(la); 

  return(error);
}


/****************************************************************************
 *
 * Start measurement and wait for block ready.
 *
 * Returns error for a timeout.
 *
 ***************************************************************************/
SHORTSIZ16 i1430_start_meas_and_wait(SHORTSIZ16 la) 
{
  SHORTSIZ16 error, status;
  
  error = i1430_pull_sync(la, MEAS_CONTROL_IDLE_MODE);
  if(error) return(error);

  error = i1430_pull_sync(la, MEAS_CONTROL_IDLE_MODE);
  if(error) return(error);
  
  error = i1430_release_sync(la, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);

  error = i1430_pull_sync(la, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);
  
  error = i1430_release_sync(la, MEAS_CONTROL_NORMAL_MODE);
  if(error) return(error);

  /* wait for data to be ready */
  error = i1430_wait_for_block_ready_la(la, &status);

  return(error);
}

